#pragma once
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstdlib>
#include <unordered_map>
#include "../comm/expand_file_exist.hpp"
#include "../comm/log.hpp"
#include "include/mysql.h"
using namespace Log;
using namespace expand;
// model模块提供对数据的处理
// 添加题解功能

// model_db模块代码重叠，有空尽量重构

struct Question
{
    int number;        // 题目编号
    std::string name;  // 题目名
    std::string level; // 难度
    std::string description; // 题目的详细描述
    std::string head;        // 预定义的头文件
    std::string tail;        // 测试代码
    std::string body;        // 给用户的代码
    std::string solve; // 题解
    int cpu_limit;     // 时间限制
    int mem_limit;     // 空间限制
};

// 题解信息
struct Solve
{
    int number;             // 题目编号
    std::string title;      // 题解名
    std::string solve;      // 题解正文
    std::string solve_code; // 题解代码
    std::string author;     // 题解作者
};

// 帖子信息
struct Discussion
{
    int id;                  // 帖子编号
    std::string maintext;    // 正文
    std::string author;      // 帖子的作者
    std::string desc;        // 帖子简洁的描述
    std::string title;       // 帖子的标题
};

// 数据库表
const std::string table1_name = "oj_questions";
const std::string table2_name = "oj_solves";
const std::string table3_name = "oj_users";
const std::string table4_name = "oj_discussions";

// 数据库信息
const std::string host = "127.0.0.1";
const std::string user = "oj_client";
const std::string password = "123456";
const std::string db = "oj";
const int port = 3306;


class Model
{
public:
    Model()
    {}
    
    bool querySql(std::string request, std::vector<Question> * out)
    {
        // 初始化
        MYSQL *my = mysql_init(nullptr);
        if (nullptr == mysql_real_connect(my, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0))
        {
            logMessage(FATAL,"%s, [%s,%d]", "访问数据库失败", __FILE__, __LINE__);
            return false;
        }

        // 设置编码
        mysql_set_character_set(my, "utf8");

        logMessage(DEBUG, "%s", "连接数据库成功！！！");

        // 执行sql语句
        if (0 != mysql_query(my, request.c_str()))
        {
            logMessage(FATAL, "%s, [%s,%d]", "sql语句执行失败!!!", __FILE__, __LINE__);
            return false;
        }

        //提取结果
        MYSQL_RES *res = mysql_store_result(my);

        // 分析结果
        int rows = mysql_num_rows(res);
        int cols = mysql_num_fields(res);

        for (int i = 0; i < rows; ++i)
        {
            MYSQL_ROW row = mysql_fetch_row(res);
            Question q;
            q.number = atoi(row[0]);
            q.name = row[1];
            q.level = row[2];
            q.description = row[3];
            q.head = row[4];
            q.body = row[5];
            q.tail = row[6];
            q.cpu_limit = atoi(row[7]);
            q.mem_limit = atoi(row[8]);

            q.solve = row[9];

            out->push_back(q);
        }
        logMessage(DEBUG, "%s, [%s,%d]", "查询题目完成!!!", __FILE__, __LINE__);
        // free
        free(res);
        mysql_close(my);

        return true;
    }

    bool getAllQuestions(std::vector<Question> *out)
    {
        std::string request = "select * from ";
        request += table1_name;       
        return querySql(request, out);
    }

    bool getOneQuestion(int number, Question *out)
    {
        bool res = false;
        std::string request = "select * from ";
        request += table1_name;
        request += " where Id=";
        request += std::to_string(number);        
        std::vector<Question> result;
        if (querySql(request, &result))
        {
            if (result.size() == 1)
            {
                *out = result[0];
                res = true;
            }
        }
        return res;
    }


    bool solveQuerySql(std::string request, Solve* out)
    {
        MYSQL *my = mysql_init(nullptr);
        if (nullptr == mysql_real_connect(my, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0))
        {
            logMessage(FATAL, "%s, [%s,%d]", "访问数据库失败", __FILE__, __LINE__);
            return false;
        }

        // 设置编码
        mysql_set_character_set(my, "utf8");

        logMessage(DEBUG, "%s", "连接数据库成功！！！");

        // 执行sql语句
        if (0 != mysql_query(my, request.c_str()))
        {
            logMessage(FATAL, "%s, [%s,%d]", "sql语句执行失败!!!", __FILE__, __LINE__);
            return false;
        }

        // 提取结果
        MYSQL_RES *res = mysql_store_result(my);

        // 分析结果

        MYSQL_ROW row = mysql_fetch_row(res);
        Solve s;
        s.number = atoi(row[0]);
        s.title = row[1];
        s.solve = row[2];
        s.solve_code = row[3];
        s.author = row[4];

        (*out) = s;  // MENTION!!!
        logMessage(DEBUG, "%s, [%s,%d]", "查询题解完成!!!", __FILE__, __LINE__);
        // free
        free(res);
        mysql_close(my);

        return true;
    }

    bool getSolve(int number, Solve* out)
    {
        // 构造sql语句
        std::string request = "select * from ";
        request += table2_name;
        request += " where id= ";
        request += std::to_string(number);
        
        return solveQuerySql(request, out);
    }

    bool toRegister(const std::string user_name, const std::string user_email, const std::string user_password, std::string& err_information)
    {
        // 查询是否存在同名用户
        std::string request = "select * from ";
        request += table3_name;
        request += " where uname=\"";
        request += user_name;
        request += "\"";
        logMessage(DEBUG, "%s, %s, [%s,%d]", "sql语句: ", request.c_str(), __FILE__, __LINE__);

        //
        MYSQL *my = mysql_init(nullptr);
        if (nullptr == mysql_real_connect(my, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0))
        {
            logMessage(FATAL, "%s, [%s,%d]", "访问数据库失败", __FILE__, __LINE__);
            return false;
        }

        // 设置编码
        mysql_set_character_set(my, "utf8");

        logMessage(DEBUG, "%s", "连接数据库成功！！！");

        // 执行sql语句
        if (0 != mysql_query(my, request.c_str()))
        {
            logMessage(FATAL, "%s, [%s,%d]", "sql语句执行失败!!!", __FILE__, __LINE__);
            return false;
        }

        MYSQL_RES *res = mysql_store_result(my);
        int rows = mysql_num_rows(res);

        if (rows > 0)
        {
            logMessage(FATAL, "%s, [%s,%d]", "存在同名用户!!!", __FILE__, __LINE__);
            err_information = "存在同名用户, 请换一个用户名";
            return false;
        }

        // 添加到数据库中
        request = "insert into ";
        request += table3_name;
        request += " values(\"";
        request += user_name;
        request += "\", \"";
        request += user_password;
        request += "\", \"";
        request += user_email;
        request += "\")";

        if (0 != mysql_query(my, request.c_str()))
        {
            logMessage(FATAL, "%s, [%s,%d]", "sql语句执行失败!!!", __FILE__, __LINE__);
            return false;
        }

        logMessage(FATAL, "%s, %s %s, [%s,%d]", "用户: ", user_name, "添加成功!!!", __FILE__, __LINE__);

        free(res);
        mysql_close(my);
        return true;
    }

    bool toLogin(const std::string user_name, const std::string user_password, std::string &err_information)
    {
        // 查询是否存在同名用户
        std::string request = "select * from ";
        request += table3_name;
        request += " where uname=\"";
        request += user_name;
        request += "\"";
        logMessage(DEBUG, "%s, %s, [%s,%d]", "sql语句: ", request.c_str(), __FILE__, __LINE__);

        //
        MYSQL *my = mysql_init(nullptr);
        if (nullptr == mysql_real_connect(my, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0))
        {
            logMessage(FATAL, "%s, [%s,%d]", "访问数据库失败", __FILE__, __LINE__);
            return false;
        }

        // 设置编码
        mysql_set_character_set(my, "utf8");

        logMessage(DEBUG, "%s", "连接数据库成功！！！");

        // 执行sql语句
        if (0 != mysql_query(my, request.c_str()))
        {
            logMessage(FATAL, "%s, [%s,%d]", "sql语句执行失败!!!", __FILE__, __LINE__);
            return false;
        }

        MYSQL_RES *res = mysql_store_result(my);
        int rows = mysql_num_rows(res);

        if (rows == 0)
        {
            logMessage(FATAL, "%s, [%s,%d]", "存在同名用户!!!", __FILE__, __LINE__);
            err_information = "当前用户尚未注册，请先注册";
            return false;
        }

        // 用户存在，检查密码是否一致
        MYSQL_ROW row = mysql_fetch_row(res);
        if (user_password != row[1])
        {
            std::cout << "password: " << password << " row[1]: " << row[1] << std::endl;
            logMessage(FATAL, "%s, [%s,%d]", "密码输入错误，请重新输入!!!", __FILE__, __LINE__);
            err_information = "密码输入错误，请重新输入";
            return false;
        }

        logMessage(FATAL, "%s, %s %s, [%s,%d]", "用户: ", user_name.c_str(), "登录成功!!!", __FILE__, __LINE__);

        // free
        free(res);
        mysql_close(my);
        return true;
    }

    bool discussionSql(std::string request, std::vector<Discussion> *out)
    {
        // 初始化
        MYSQL *my = mysql_init(nullptr);
        if (nullptr == mysql_real_connect(my, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0))
        {
            logMessage(FATAL, "%s, [%s,%d]", "访问数据库失败", __FILE__, __LINE__);
            return false;
        }

        // 设置编码
        mysql_set_character_set(my, "utf8");

        logMessage(DEBUG, "%s", "连接数据库成功！！！");

        // 执行sql语句
        if (0 != mysql_query(my, request.c_str()))
        {
            logMessage(FATAL, "%s, [%s,%d]", "sql语句执行失败!!!", __FILE__, __LINE__);
            return false;
        }

        // 提取结果
        MYSQL_RES *res = mysql_store_result(my);

        // 分析结果
        int rows = mysql_num_rows(res);
        int cols = mysql_num_fields(res);

        for (int i = 0; i < rows; ++i)
        {
            MYSQL_ROW row = mysql_fetch_row(res);
            Discussion q;
            q.id = atoi(row[0]);
            q.maintext = row[1];
            q.author = row[2];
            q.desc = row[3];
            q.title = row[4];


            out->push_back(q);
        }
        logMessage(DEBUG, "%s, [%s,%d]", "查询所有帖子完成!!!", __FILE__, __LINE__);
        // free
        free(res);
        mysql_close(my);

        return true;
    }

    // 提取所有的帖子
    bool getAllDiscussion(std::vector<Discussion> *out)
    {
        std::string request = "select * from ";
        request += table4_name;
        return discussionSql(request, out);
    }

    // 提取特定帖子
    bool getADiscussion(int number, Discussion* out)
    {
        std::vector<Discussion> d;
        bool res = false;
        std::string request = "select * from ";
        request += table4_name;
        request += " where id=";
        request += std::to_string(number);
        
        logMessage(DEBUG, "%s, %s, [%s,%d]", "sql语句: ", request.c_str(), __FILE__, __LINE__);
        if (discussionSql(request, &d))
        {
            if (d.size() == 1)
            {
                (*out) = d[0];
                res = true;
            }
        }

        return res;
    }

    //添加帖子到数据库中
    bool addArticleToDB(std::string title, std::string author, std::string maintext, std::string err_information)
    {
        // 给帖子编上序号，同时需要提取10个字简介，可以添加发文时间（TODO）
        // 1. 获取当前数据库的帖子数量
        std::string request = "select count(*) from ";
        request += table4_name;
        // 初始化
        MYSQL *my = mysql_init(nullptr);
        if (nullptr == mysql_real_connect(my, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0))
        {
            logMessage(FATAL, "%s, [%s,%d]", "访问数据库失败", __FILE__, __LINE__);
            err_information = "error";
            return false;
        }

        // 设置编码
        mysql_set_character_set(my, "utf8");

        logMessage(DEBUG, "%s", "连接数据库成功！！！");

        // 执行sql语句
        if (0 != mysql_query(my, request.c_str()))
        {
            logMessage(FATAL, "%s, [%s,%d]", "sql语句执行失败!!!", __FILE__, __LINE__);
            err_information = "error";
            return false;
        }

        // 提取结果
        MYSQL_RES *res = mysql_store_result(my);
        MYSQL_ROW row = mysql_fetch_row(res);
        // 分析结果
        int id = atoi(row[0]) + 1;
        std::string desc = maintext.size() > 10 ? maintext.substr(0, 11) + "..." : maintext;

        // 处理提交的正文
        // maintext.erase(std::remove(maintext.begin(), maintext.end(), '\n'), maintext.end());
        logMessage(DEBUG, "size: %d", maintext.size());

        // 插入数据库
        request = "insert into ";
        request += table4_name;
        request += " values(\"";
        request += std::to_string(id);
        request += "\", \"";
        request += maintext;
        request += "\", \"";
        request += author;
        request += "\", \"";
        request += desc;
        request += "\", \"";
        request += title;
        request += "\")";

        std::cout << "插入语句: " << request << std::endl;

        if (0 != mysql_query(my, request.c_str()))
        {
            logMessage(FATAL, "%s, [%s,%d]", "sql语句执行失败!!!", __FILE__, __LINE__);
            err_information = "error";

            return false;
        }

        logMessage(DEBUG, "%s, [%s,%d]", "发布帖子成功!!!", __FILE__, __LINE__);

        free(res);
        mysql_close(my);

        return true;
    }

    ~Model(){}
};